﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace WeakScript
{
    class OperationRoom
    { 
        public delegate void VarBiOp(WeakVar a, WeakVar b, WeakVar res);        
        public static Dictionary<string, long> TypesMap;

        public List<WeakVar>            HeapStack;
        public List<WeakObject>         ObjectStack;
        public Stack<WeakVar>           WorkStack;
        public Stack<string> NamespaceStack;

        public string NamespacePrefix { 
            
            get{return NamespaceStack.Peek();}
        
            set{}
        }

        public void OpenNamespace(){
            string peek = NamespaceStack.Pop();
            peek = peek.Insert(0, Global.GetUK() + ".");
            NamespaceStack.Push(peek);
        }

        public void CloseNamespace(){
            string peek = NamespaceStack.Pop();
            peek = peek.Remove(0, peek.IndexOf('.')+1);
            NamespaceStack.Push(peek);
        }

        public void SaveAndLeaveNamespace()
        {
            NamespaceStack.Push("root");
        }

        public void RestoreNamespace()
        {
            NamespaceStack.Pop();
        }

        public OperationRoom()
        {
            HeapStack = new List<WeakVar>();
            ObjectStack  = new List<WeakObject>();
            WorkStack = new Stack<WeakVar>();
            NamespaceStack = new Stack<string>();

            TypesMap = new Dictionary<string, long>();
            TypesMap["bool"] =      (long)WeakVar.InternalType.BOOL;
            TypesMap["float"] =     (long)WeakVar.InternalType.FLT;
            TypesMap["char"] =      (long)WeakVar.InternalType.CHR;
            TypesMap["string"] =    (long)WeakVar.InternalType.STR;
            TypesMap["ref"] =       (long)WeakVar.InternalType.REF;

            Registr.TypesNames.AddRange(TypesMap.Keys.ToArray());
            NamespaceStack.Push("root");
        }

        public WeakVar CreateTempVar()
        {
            var var = new WeakVar();
            var.Name += Global.GetUK();
            return var;
        }

        public WeakVar CreateHeapVar()
        {
            var var = new WeakVar();
            HeapStack.Add(var);
            return var;
        }

        const string HeapPrefix = "heap_";
        public WeakVar CreateNamedHeapVar(string name)
        {
            WeakVar var = CreateHeapVar();
            var.Name = HeapPrefix + NamespacePrefix + name;
            return var;
        }

        public WeakVar GetNamedHeapVar(string name)
        {
            string peek = NamespaceStack.Pop();
            WeakVar var = HeapStack.Find(p => p.Name == HeapPrefix + peek + name);

            while(var==null && peek!="root"){
                peek = peek.Remove(0, peek.IndexOf('.') + 1);
                var = HeapStack.Find(p => p.Name == HeapPrefix + peek + name);
            }           
 
            if(var == null) throw new Exception("Variable not found "+ name);
            return var;
        }

        public WeakVar CreateBlankVar()
        {
            var var = new WeakVar();        
            return var;
        }

        public void SetType(WeakVar var,string typename)
        {
            var.TypeID = Registr.TypesMap[typename];
            SetInnerType(var, Registr.GetInnerType(typename));
        }

        public void SetInnerType(WeakVar var, WeakVar.InternalType type)
        {
            var.InnerType = type;
        }
        public void PushVar(WeakVar var){
            WorkStack.Push(var);
        }

        public WeakVar PopVar()
        {
            return WorkStack.Pop();
        }

        public WeakVar PeekVar()
        {
            return WorkStack.Peek();
        }

        public void PushMarker(string name)
        {
            WeakVar var = new WeakVar();
            var.Name = name;
            var.InnerType = WeakVar.InternalType.MRK;
            WorkStack.Push(var);
        }
       
        public static void Set(WeakVar a, WeakVar b)
        {
            if (a.InnerType == (long)WeakVar.InternalType.NONE)
            {
                a.InnerType = b.InnerType;
            }

            if (a.InnerType == b.InnerType)
            {
                if (a.InnerType != WeakVar.InternalType.REF)
                {
                    if (a.InnerType == WeakVar.InternalType.CHR) a.charValue = b.charValue;
                    if (a.InnerType == WeakVar.InternalType.FLT)a.floatValue = b.floatValue;                  
                    if (a.InnerType == WeakVar.InternalType.STR) a.stringValue = b.stringValue;
                }
                else
                {
                    if (a.refValue != null) a.refValue.LinkCount--;
                    a.refValue = b.refValue;
                    a.refValue.LinkCount++;
                }
            }
            else
            {
                throw new Exception("Different types!");
            }
        }

        public void Clean(WeakVar a)
        {
            if (a.InnerType == WeakVar.InternalType.REF)
            {
                a.refValue.LinkCount--;
            }

            a.charValue = (char)0;
            a.floatValue = 0;
            a.stringValue = "";

            a.refValue = null;
        }

        public void ExecuteBinaryOperation(VarBiOp op)
        {
            WeakVar b = WorkStack.Pop();
            WeakVar a = WorkStack.Pop();

            if (a.InnerType == b.InnerType)
                if (a.InnerType != WeakVar.InternalType.REF)
                {
                    WeakVar res = CreateTempVar();
                    op(a, b, res);
                    WorkStack.Push(res);
                    return;
                }
            throw new Exception("Different types!");
        }
    }
}
